package main

import "fmt"

type Map map[string]string

func (m Map) Print() {
	for _, key := range m {
		fmt.Println(key)
	}
}

// 底层类型是 slice 和 map 等支持range的类型的字面量，新类型依然可以使用range方式
type iMap Map

func (m iMap) Print() {
	for _, key := range m {
		fmt.Println(key)
	}
}

type slice []int

func (s slice) Print() {
	for _, v := range s {
		fmt.Println(v)
	}
}

type MyInt int

type SliceInt []int

func (s SliceInt) Sum() int {
	sum := 0
	for _, v := range s {
		sum += v
	}
	return sum
}

func SliceInt_Sum(s SliceInt) int {
	sum := 0
	for _, v := range s {
		sum += v
	}
	return sum
}

type T struct {
	a int
}

func (t T) Get() int {
	return t.a
}

func (t *T) Set(i int) {
	t.a = i
}

func (t *T) Print() {
	fmt.Printf("%p, %v, %d \n", t, t, t.a)
}

type Int int

func (a Int) Max(b Int) Int {
	if a >= b {
		return a
	} else {
		return b
	}
}

func (i *Int) Set(a Int) {
	*i = a
}

func (i Int) Print() {
	fmt.Printf("value = %d \n", i)
}

type X struct {
	a int
}

type Y struct {
	X
	b int
}

type Z struct {
	Y
	c int
}

func (x X) Print() {
	fmt.Printf("In X, a = %d\n", x.a)
}

func (x X) XPrint() {
	fmt.Printf("In X, a = %d\n", x.a)
}

func (y Y) Print() {
	fmt.Printf("In Y, b = %d\n", y.b)
}

func (z Z) Print() {
	fmt.Printf("In Z, c = %d\n", z.c)

	// 显式的完全路径调用内嵌字段的方法
	z.Y.Print()
	z.Y.X.Print()
}

func main() {
	mp := make(map[string]string, 10)
	mp["hi"] = "tata"

	var ma Map = mp
	ma.Print()

	//var im iMap = ma
	//im.Print()

	var i interface {
		Print()
	} = ma

	i.Print()

	s1 := []int{1, 2, 3}
	var s2 slice
	s2 = s1
	s2.Print()

	// 3.1.4 强制类型转换
	//var a T = (T) (x)
	// 非常量类型的变量x可以强制转化并传递给T：
	// x 可以直接赋值给T类型变量
	// x 的类型和 T 具有相同的底层类型

	/*
	   以下内容是字符串和字节切片之间的转换
	*/
	s := "Hello, 世界！"
	var a []byte
	a = []byte(s)
	var b string
	b = string(a)
	var c []rune
	c = []rune(s)

	fmt.Printf("%T\n", s) // string
	fmt.Printf("%T\n", a) // []uint8 是 byte的别名
	fmt.Printf("%T\n", b) // string
	fmt.Printf("%T\n", c) // []int32

	println(s)
	println(a)
	println(b)
	println(c)

	var si SliceInt = []int{1, 2, 3, 4}
	fmt.Println("si.Sum(): ", si.Sum())
	fmt.Println("SliceInt_Sum(si): ", SliceInt_Sum(si))

	var int_a MyInt = 10
	var int_b MyInt = 10

	int_c := int_a + int_b
	int_d := int_a * int_b

	fmt.Printf("%d\n", int_c)
	fmt.Printf("%d\n", int_d)

	// 3.3.1 方法调用
	var t = &T{} // 这里是 &T{} ， 不是 &T()

	t.Set(2)
	println(t.Get())

	// 3.3.2 方法值 method value
	f := t.Set
	f(2)
	t.Print()

	f(3)
	t.Print()

	// 3.3.3 方法表达式

	t1 := T{a: 1}
	println(t1.Get())    // 普通调用
	println((T).Get(t1)) // 方法表达式

	f1 := T.Get
	println(f1(t1)) // 方法表达式

	f2 := (T).Get
	println(f2(t1)) // 方法表达式

	// 以下两个方法表达式的赋值语句
	(*T).Set(&t1, 2)
	ff := (*T).Set
	ff(&t1, 3)

	f3 := (T).Get
	println(f3(t1))

	// 以上结论：GO 的方法底层都是基于函数实现的，只是语法格式不同，本质是一样的。

	// 3.3.4 方法集
	var var_a Int = 10
	var var_b Int = 20

	var_c := var_a.Max(var_b)
	var_c.Print()
	(&var_c).Print() // 内部被编译为：var_c.Print()

	var_a.Set(20) // 内部被编译为：(&var_a).Set(20)
	var_a.Print()

	(&var_a).Set(20)
	var_a.Print()

	// 以上结论：在直接使用类型实例调用类型的方法时，无论值类型变量还是指针类型变量，都可以调用类型的所有方法，
	// 原因：编译器在编译期间能够识别出这种调用，并做了自动转换。

	// 3.4 组合和方法集

	// 在 struct 的多层嵌套中，不同的嵌套层次可以有相同的字段，此时最好使用完全路径进行访问和初始化，
	// 在实际数据结构的定义中应该尽量避开相同的字段，以免在使用中实现出现歧义
	x := X{a: 1}

	y := Y{
		X: x,
		b: 2,
	}

	z := Z{
		Y: y,
		c: 3,
	}

	println(z.a, z.X.a, z.Y.X.a)

	//z = Z{}
	//z.a = 2
	//
	//println(z.a, z.X.a, z.Y.X.a)

	// 内嵌字段的方法调用
	// 从外向内查找，最先找到的是 Z 的 Print()方法
	z.Print()

	// 从外向内查找，最后找到的是 Z 的 XPrint()方法
	z.XPrint()
	z.Y.XPrint()
}
